// SPDX-License-Identifier: GPL-2.0
/*
 * gpadc_sensor_csp.c - gpadc csp sensor operation
 *
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include "csp_gpadc.h"

#define UNIQUE_ID		0x7
#define T_SENSOR_0		0x21
#define T_SENSOR_1		0x22
#define T_SENSOR_2		0x23
#define T_SENSOR_3		0x24
#define T_SENSOR_4		0x25

static int offset1 = 0, offset2 = 0;

/* Set temperature sensor sample rate */
void csp_sensor_set_sample_rate(void *base, SENSOR_SAMPLE_RATE sr)
{
	reg_gpadc_tsc_t reg;

	reg.val = READREG32(base + GPADC_TSC_OFF);
	reg.bits.sr = sr;

	WRITEREG32(base + GPADC_TSC_OFF, reg.val);
}

/* Set temperature sensor sample data average enable */
void csp_sensor_set_sda_en(void *base, u32 en)
{
	reg_gpadc_tsc_t reg;

	reg.val = READREG32(base + GPADC_TSC_OFF);
	reg.bits.avge = en;

	WRITEREG32(base + GPADC_TSC_OFF, reg.val);
}

/* Set temperature sensor enable */
void csp_sensor_set_en(void *base, u32 en)
{
	reg_gpadc_tsc_t reg;

	reg.val = READREG32(base + GPADC_TSC_OFF);
	reg.bits.tsen = en;

	WRITEREG32(base + GPADC_TSC_OFF, reg.val);
}

/* Set temperature sensor data, down or up threshold interrupt enable  */
void csp_sensor_set_int_en(void *base, u32 source, u32 en)
{
	reg_gpadc_tsie_t reg;

	reg.val = READREG32(base + GPADC_TSIE_OFF);
	if (en)
		reg.val |= source;
	else
		reg.val &= ~source;

	WRITEREG32(base + GPADC_TSIE_OFF, reg.val);
}

/* Get temperature sensor interrupt pending */
u32 csp_sensor_get_int_pend(void *base, u32 source)
{
	reg_gpadc_tsis_t reg;

	reg.val = READREG32(base + GPADC_TSIS_OFF);
	return reg.val & source;
}

/* Clear temperature sensor interrupt pending status */
void csp_sensor_clr_int_pend(void *base, u32 source)
{
	reg_gpadc_tsic_t reg;

	reg.val = 0;
	reg.val |= source;

	WRITEREG32(base + GPADC_TSIC_OFF, reg.val);
}

#ifdef CONFIG_LOMBO_THERMAL
int csp_get_slope(int id)
{
	int slope = 0;
	u64 unique_id = 0;

	unique_id = csp_get_efuse_value(UNIQUE_ID);
	if ((unique_id & 0xf) == 0xf)
		slope = 68000;
	else
		slope = 69000;

	return slope;
}

int csp_calibration_offset(int id)
{
	int offset = 0, offset_1 = 0;
	u32 t_senor_id;

	switch (id) {
	case 0:
		t_senor_id = T_SENSOR_0;
		break;
	case 1:
		t_senor_id = T_SENSOR_1;
		break;
	case 2:
		t_senor_id = T_SENSOR_2;
		break;
	case 3:
		t_senor_id = T_SENSOR_3;
		break;
	case 4:
		t_senor_id = T_SENSOR_4;
		break;
	default:
		t_senor_id = T_SENSOR_0;
		break;
	}

	offset = (int)csp_get_efuse_value(t_senor_id);
	if (id != 0) {
		offset_1 = (int)csp_get_efuse_value(T_SENSOR_0);
		offset = (offset_1 & 0xF00) + offset;
		offset_1 = offset - offset_1;
		if (offset_1 > 0x80)
			offset = offset - 0x100;
		else if (offset_1 < -0x80)
			offset = offset + 0x100;
	}

	/* for sensor0, record offset */
	if (id == 0) {
		offset1 = (int)csp_get_efuse_value(T_SENSOR_1);
		offset_1 = (int)csp_get_efuse_value(T_SENSOR_0);
		offset1 = (offset_1 & 0xF00) + offset1;
		offset_1 = offset1 - offset_1;
		if (offset_1 > 0x80)
			offset1 = offset1 - 0x100;
		else if (offset_1 < -0x80)
			offset1 = offset1 + 0x100;

		offset2 = (int)csp_get_efuse_value(T_SENSOR_2);
		offset_1 = (int)csp_get_efuse_value(T_SENSOR_0);
		offset2 = (offset_1 & 0xF00) + offset2;
		offset_1 = offset2 - offset_1;
		if (offset_1 > 0x80)
			offset2 = offset2 - 0x100;
		else if (offset_1 < -0x80)
			offset2 = offset2 + 0x100;

		if (offset1 == 0)
			offset1 = 360;
		if (offset2 == 0)
			offset2 = 360;
		PRT_INFO("offset1:0x%x, offset2:0x%x", offset1, offset2);
	}

	/* set default calibration value */
	if (offset == 0)
		offset = 360;

	PRT_DBG("value:0x%x, t_senor_id:0x%x", offset, t_senor_id);
	return offset;
}

long csp_sensor_get_temp(int id, void __iomem *base, int slope, int offset)
{
	long temp_uint = 0;
	long long temp = 0;
	u64 value = 0, offset_0 = 0;
	static int warn_cnt[8];
	/* record value for sensor1 */
	long temp_uint1, temp_uint2;
	long long temp1 = 0, temp2 = 0;
	u64 value1 = 0, value2 = 0;

	value = readl(base + GPADC_TS_X_DAT(id));
	if (value == 0) {
		warn_cnt[id]++;
		if (warn_cnt[id] < 2)
			value = 2000;
		else
			WARN_ON(value == 0);
	}

	offset_0 = csp_get_efuse_value(T_SENSOR_0);
	if (offset_0 == 0)
		offset_0 = 360;

	if (offset_0 >= 600) {
		if (slope == 69000)
			temp = 142 * 1000 * 1000 + slope * (1120 - value);
		else
			temp = 142 * 1000 * 1000 + slope * (offset - value);
		temp_uint = (long)div64_s64(temp, 1000LL);
	} else {
		temp = 180LL * 1000000 * 1000000 +
				58180 * (offset - value) * 1000000 -
				1406835 * value * value;
		temp_uint = (long)div64_s64(temp, 1000LL * 1000000);
	}

	/* for sensor0, record temp */
	if (id == 0) {
		/* sensor1 */
		value1 = readl(base + GPADC_TS_X_DAT(1));
		if (value1 != 0) {
			if (offset_0 >= 600) {
				if (slope == 69000)
					temp1 = 142 * 1000 * 1000 + slope * (1120 - value1);
				else
					temp1 = 142 * 1000 * 1000 + slope * (offset1 - value1);
				temp_uint1 = (long)div64_s64(temp1, 1000LL);
			} else {
				temp1 = 180LL * 1000000 * 1000000 +
						58180 * (offset1 - value1) * 1000000 -
						1406835 * value1 * value1;
				temp_uint1 = (long)div64_s64(temp1, 1000LL * 1000000);
			}
			/* if sensor1 temp > sensor0 temp */
			if (temp_uint1 > temp_uint) {
				PRT_DBG("sensor1 temp:%ld > sensor0 temp:%ld\n", temp_uint1, temp_uint);
				temp_uint = temp_uint1;
			}
		}

		/* sensor2 */
		value2 = readl(base + GPADC_TS_X_DAT(2));
		if (value2 != 0) {
			if (offset_0 >= 600) {
				if (slope == 69000)
					temp2 = 142 * 1000 * 1000 + slope * (1120 - value2);
				else
					temp2 = 142 * 1000 * 1000 + slope * (offset2 - value2);
				temp_uint2 = (long)div64_s64(temp2, 1000LL);
			} else {
				temp2 = 180LL * 1000000 * 1000000 +
						58180 * (offset2 - value2) * 1000000 -
						1406835 * value2 * value2;
				temp_uint2 = (long)div64_s64(temp2, 1000LL * 1000000);
			}

			/* if sensor1 temp > sensor0 temp */
			if (temp_uint2 > temp_uint) {
				PRT_DBG("sensor2 temp:%ld > sensor0 temp:%ld\n", temp_uint2, temp_uint);
				temp_uint = temp_uint2;
			}
		}

	}

	if (temp_uint >= 120000) {
		PRT_WARN("temp:%lld, value:%lld, slope:%d, offset:%d\n",
			temp, value, slope, offset);
		PRT_WARN("sensor_id:%d, addr:%px\n", id,
				base + GPADC_TS_X_DAT(id));
	}

	return temp_uint;
}

#endif  /* CONFIG_LOMBO_THERMAL */

